/* Respond.js: min/max-width media query polyfill. (c) Scott Jehl. MIT Lic. j.mp/respondjs  */
(function( w ){


        "use strict";


        //exposed namespace
        var respond = {};
        w.respond = respond;


        //define update even in native-mq-supporting browsers, to avoid errors
        respond.update = function(){};


        //define ajax obj
        var requestQueue = [],
                xmlHttp = (function() {
                        var xmlhttpmethod = false;
                        try {
                                xmlhttpmethod = new w.XMLHttpRequest();
                        }
                        catch( e ){
                                xmlhttpmethod = new w.ActiveXObject( "Microsoft.XMLHTTP" );
                        }
                        return function(){
                                return xmlhttpmethod;
                        };
                })(),


                //tweaked Ajax functions from Quirksmode
                ajax = function( url, callback ) {
                        var req = xmlHttp();
                        if (!req){
                                return;
                        }
                        req.open( "GET", url, true );
                        req.onreadystatechange = function () {
                                if ( req.readyState !== 4 || req.status !== 200 && req.status !== 304 ){
                                        return;
                                }
                                callback( req.responseText );
                        };
                        if ( req.readyState === 4 ){
                                return;
                        }
                        req.send( null );
                },
                isUnsupportedMediaQuery = function( query ) {
                        return query.replace( respond.regex.minmaxwh, '' ).match( respond.regex.other );
                };


        //expose for testing
        respond.ajax = ajax;
        respond.queue = requestQueue;
        respond.unsupportedmq = isUnsupportedMediaQuery;
        respond.regex = {
                media: /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,
                keyframes: /@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,
                comments: /\/\*[^*]*\*+([^/][^*]*\*+)*\//gi,
                urls: /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,
                findStyles: /@media *([^\{]+)\{([\S\s]+?)$/,
                only: /(only\s+)?([a-zA-Z]+)\s?/,
                minw: /\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,
                maxw: /\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,
                minmaxwh: /\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi,
                other: /\([^\)]*\)/g
        };


        //expose media query support flag for external use
        respond.mediaQueriesSupported = w.matchMedia && w.matchMedia( "only all" ) !== null && w.matchMedia( "only all" ).matches;


        //if media queries are supported, exit here
        if( respond.mediaQueriesSupported ){
                return;
        }


        //define vars
        var doc = w.document,
                docElem = doc.documentElement,
                mediastyles = [],
                rules = [],
                appendedEls = [],
                parsedSheets = {},
                resizeThrottle = 30,
                head = doc.getElementsByTagName( "head" )[0] || docElem,
                base = doc.getElementsByTagName( "base" )[0],
                links = head.getElementsByTagName( "link" ),


                lastCall,
                resizeDefer,


                //cached container for 1em value, populated the first time it's needed
                eminpx,


                // returns the value of 1em in pixels
                getEmValue = function() {
                        var ret,
                                div = doc.createElement('div'),
                                body = doc.body,
                                originalHTMLFontSize = docElem.style.fontSize,
                                originalBodyFontSize = body && body.style.fontSize,
                                fakeUsed = false;


                        div.style.cssText = "position:absolute;font-size:1em;width:1em";


                        if( !body ){
                                body = fakeUsed = doc.createElement( "body" );
                                body.style.background = "none";
                        }


                        // 1em in a media query is the value of the default font size of the browser
                        // reset docElem and body to ensure the correct value is returned
                        docElem.style.fontSize = "100%";
                        body.style.fontSize = "100%";


                        body.appendChild( div );


                        if( fakeUsed ){
                                docElem.insertBefore( body, docElem.firstChild );
                        }


                        ret = div.offsetWidth;


                        if( fakeUsed ){
                                docElem.removeChild( body );
                        }
                        else {
                                body.removeChild( div );
                        }


                        // restore the original values
                        docElem.style.fontSize = originalHTMLFontSize;
                        if( originalBodyFontSize ) {
                                body.style.fontSize = originalBodyFontSize;
                        }




                        //also update eminpx before returning
                        ret = eminpx = parseFloat(ret);


                        return ret;
                },


                //enable/disable styles
                applyMedia = function( fromResize ){
                        var name = "clientWidth",
                                docElemProp = docElem[ name ],
                                currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[ name ] || docElemProp,
                                styleBlocks        = {},
                                lastLink = links[ links.length-1 ],
                                now = (new Date()).getTime();


                        //throttle resize calls
                        if( fromResize && lastCall && now - lastCall < resizeThrottle ){
                                w.clearTimeout( resizeDefer );
                                resizeDefer = w.setTimeout( applyMedia, resizeThrottle );
                                return;
                        }
                        else {
                                lastCall = now;
                        }


                        for( var i in mediastyles ){
                                if( mediastyles.hasOwnProperty( i ) ){
                                        var thisstyle = mediastyles[ i ],
                                                min = thisstyle.minw,
                                                max = thisstyle.maxw,
                                                minnull = min === null,
                                                maxnull = max === null,
                                                em = "em";


                                        if( !!min ){
                                                min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 );
                                        }
                                        if( !!max ){
                                                max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 );
                                        }


                                        // if there's no media query at all (the () part), or min or max is not null, and if either is present, they're true
                                        if( !thisstyle.hasquery || ( !minnull || !maxnull ) && ( minnull || currWidth >= min ) && ( maxnull || currWidth <= max ) ){
                                                if( !styleBlocks[ thisstyle.media ] ){
                                                        styleBlocks[ thisstyle.media ] = [];
                                                }
                                                styleBlocks[ thisstyle.media ].push( rules[ thisstyle.rules ] );
                                        }
                                }
                        }


                        //remove any existing respond style element(s)
                        for( var j in appendedEls ){
                                if( appendedEls.hasOwnProperty( j ) ){
                                        if( appendedEls[ j ] && appendedEls[ j ].parentNode === head ){
                                                head.removeChild( appendedEls[ j ] );
                                        }
                                }
                        }
                        appendedEls.length = 0;


                        //inject active styles, grouped by media type
                        for( var k in styleBlocks ){
                                if( styleBlocks.hasOwnProperty( k ) ){
                                        var ss = doc.createElement( "style" ),
                                                css = styleBlocks[ k ].join( "\n" );


                                        ss.type = "text/css";
                                        ss.media = k;


                                        //originally, ss was appended to a documentFragment and sheets were appended in bulk.
                                        //this caused crashes in IE in a number of circumstances, such as when the HTML element had a bg image set, so appending beforehand seems best. Thanks to @dvelyk for the initial research on this one!
                                        head.insertBefore( ss, lastLink.nextSibling );


                                        if ( ss.styleSheet ){
                                                ss.styleSheet.cssText = css;
                                        }
                                        else {
                                                ss.appendChild( doc.createTextNode( css ) );
                                        }


                                        //push to appendedEls to track for later removal
                                        appendedEls.push( ss );
                                }
                        }
                },
                //find media blocks in css text, convert to style blocks
                translate = function( styles, href, media ){
                        var qs = styles.replace( respond.regex.comments, '' )
                                        .replace( respond.regex.keyframes, '' )
                                        .match( respond.regex.media ),
                                ql = qs && qs.length || 0;


                        //try to get CSS path
                        href = href.substring( 0, href.lastIndexOf( "/" ) );


                        var repUrls = function( css ){
                                        return css.replace( respond.regex.urls, "$1" + href + "$2$3" );
                                },
                                useMedia = !ql && media;


                        //if path exists, tack on trailing slash
                        if( href.length ){ href += "/"; }


                        //if no internal queries exist, but media attr does, use that
                        //note: this currently lacks support for situations where a media attr is specified on a link AND
                                //its associated stylesheet has internal CSS media queries.
                                //In those cases, the media attribute will currently be ignored.
                        if( useMedia ){
                                ql = 1;
                        }


                        for( var i = 0; i < ql; i++ ){
                                var fullq, thisq, eachq, eql;


                                //media attr
                                if( useMedia ){
                                        fullq = media;
                                        rules.push( repUrls( styles ) );
                                }
                                //parse for styles
                                else{
                                        fullq = qs[ i ].match( respond.regex.findStyles ) && RegExp.$1;
                                        rules.push( RegExp.$2 && repUrls( RegExp.$2 ) );
                                }


                                eachq = fullq.split( "," );
                                eql = eachq.length;


                                for( var j = 0; j < eql; j++ ){
                                        thisq = eachq[ j ];


                                        if( isUnsupportedMediaQuery( thisq ) ) {
                                                continue;
                                        }


                                        mediastyles.push( {
                                                media : thisq.split( "(" )[ 0 ].match( respond.regex.only ) && RegExp.$2 || "all",
                                                rules : rules.length - 1,
                                                hasquery : thisq.indexOf("(") > -1,
                                                minw : thisq.match( respond.regex.minw ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ),
                                                maxw : thisq.match( respond.regex.maxw ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" )
                                        } );
                                }
                        }


                        applyMedia();
                },


                //recurse through request queue, get css text
                makeRequests = function(){
                        if( requestQueue.length ){
                                var thisRequest = requestQueue.shift();


                                ajax( thisRequest.href, function( styles ){
                                        translate( styles, thisRequest.href, thisRequest.media );
                                        parsedSheets[ thisRequest.href ] = true;


                                        // by wrapping recursive function call in setTimeout
                                        // we prevent "Stack overflow" error in IE7
                                        w.setTimeout(function(){ makeRequests(); },0);
                                } );
                        }
                },


                //loop stylesheets, send text content to translate
                ripCSS = function(){


                        for( var i = 0; i < links.length; i++ ){
                                var sheet = links[ i ],
                                href = sheet.href,
                                media = sheet.media,
                                isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet";


                                //only links plz and prevent re-parsing
                                if( !!href && isCSS && !parsedSheets[ href ] ){
                                        // selectivizr exposes css through the rawCssText expando
                                        if (sheet.styleSheet && sheet.styleSheet.rawCssText) {
                                                translate( sheet.styleSheet.rawCssText, href, media );
                                                parsedSheets[ href ] = true;
                                        } else {
                                                if( (!/^([a-zA-Z:]*\/\/)/.test( href ) && !base) ||
                                                        href.replace( RegExp.$1, "" ).split( "/" )[0] === w.location.host ){
                                                        // IE7 doesn't handle urls that start with '//' for ajax request
                                                        // manually add in the protocol
                                                        if ( href.substring(0,2) === "//" ) { href = w.location.protocol + href; }
                                                        requestQueue.push( {
                                                                href: href,
                                                                media: media
                                                        } );
                                                }
                                        }
                                }
                        }
                        makeRequests();
                };


        //translate CSS
        ripCSS();


        //expose update for re-running respond later on
        respond.update = ripCSS;


        //expose getEmValue
        respond.getEmValue = getEmValue;


        //adjust on resize
        function callMedia(){
                applyMedia( true );
        }


        if( w.addEventListener ){
                w.addEventListener( "resize", callMedia, false );
        }
        else if( w.attachEvent ){
                w.attachEvent( "onresize", callMedia );
        }
})(this);

